using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace BassMati.Diagnostics
{
	/// <summary>
	/// Summary description for TraceForm.
	/// </summary>
	public class TraceForm : System.Windows.Forms.Form
	{
		private System.Windows.Forms.TextBox tbxMessages;
		private System.ComponentModel.Container components = null;

		#region - Constructors - 

		public TraceForm ( )
		{
			InitializeComponent();
		}

		public TraceForm ( string threadName )
		: this ( )
		{
			this.threadNameFilter = threadName;
			this.Text = threadName;
		}

		#endregion - Constructors - 
		
		#region - Fields and Properties -

		/// <summary>
		/// Enables the ThreadListener to access the Write method.
		/// </summary>
		public delegate void WriteDelegate ( string message, string category );
				
		/// <summary>
		/// Defines the Thread to monitor.
		/// </summary>
		public string threadNameFilter = string.Empty;

		/// <summary>
		/// If true, allows the form to close.
		/// </summary>
		protected bool canClose;

		#endregion - Fields -

		#region - Generated Code -

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.tbxMessages = new System.Windows.Forms.TextBox();
			this.SuspendLayout();
			// 
			// tbxMessages
			// 
			this.tbxMessages.Location = new System.Drawing.Point(0, 0);
			this.tbxMessages.Multiline = true;
			this.tbxMessages.Name = "tbxMessages";
			this.tbxMessages.Size = new System.Drawing.Size(288, 272);
			this.tbxMessages.TabIndex = 0;
			this.tbxMessages.Text = "";
			this.tbxMessages.ScrollBars = ScrollBars.Vertical;
			// 
			// TraceForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(288, 273);
			this.Controls.Add(this.tbxMessages);
			this.Name = "TraceForm";
			this.Text = "TraceForm";
			this.Closing += new System.ComponentModel.CancelEventHandler(this.TraceForm_Closing);
			this.ResumeLayout(false);

		}
		#endregion

		#endregion - Generated Code -

		#region - Indentation -

		/// <summary>
		/// Determines the amount of indentation.
		/// </summary>
		public int IndentLevel 
		{
			get { return this.indentLevel; }
			set 
			{
				this.indentLevel = value < 0 ? 0 : value > IndentLevelMax ? IndentLevelMax : value;
				this.indentString = new string( ' ', 2 * indentLevel );
			}
		}

		protected int indentLevel = 0;

		/// <summary>
		/// Defines the maximum indent level.
		/// </summary>
		public int IndentLevelMax = 99;
		
		/// <summary>
		/// Determines the amount of charecters used for indentation.
		/// </summary>
		public int IndentWidth 
		{
			get { return this.indentWidth; }
			set 
			{
				this.indentWidth = value < 0 ? 0 : value;
			}
		}

		protected int indentWidth = 2;
		
		/// <summary>
		/// Defines the character used for indentation.
		/// </summary>
		public char IndentChar = ' ';

		/// <summary>
		/// This is the string used for indentation on the current level.
		/// </summary>
		protected string indentString = string.Empty;

		#endregion - Indentation -

		#region - Formatting -

		/// <summary>
		/// Defines the fomatting of trace entries
		/// </summary>
		/// <remarks>
		/// A number of predifined formats are recognized. If the format name is not
		/// recognized, the value is used as custom format. The placeholder meanings 
		/// are:
		/// {0} = current timestamp
		/// {1} = indent string according to the current level
		/// {2} = category string as supplied by the caller
		/// {3} = message text as supplied by the caller
		/// </remarks>
		public string TraceEntryFormat
		{
			get { return this.formatString; }
			set 
			{
				if ( value == "Default" ) this.formatString = defaultFormatString;
				else if ( value == "Timestamp" ) this.formatString = "{0:hh:mm:ss.fff}{1} {2}: {3}";
				else if ( value == "NoIndent" ) this.formatString = "{2}: {3}";
				else this.formatString = value;
			}
		}

		protected static string defaultFormatString = "{1} {2}: {3}";

		/// <summary>
		/// Determines the layout of trace entries.
		/// </summary>
		protected string formatString = defaultFormatString;
		
		#endregion - Formatting -

		/// <summary>
		/// Write a formatted trace message to the window.
		/// </summary>
		/// <param name="message"></param>
		/// <param name="category"></param>
		public void Write ( string message, string category )
		{
			// Save values because appending text resets them.
			int selectionStart = tbxMessages.SelectionStart;
			int selectionEnd = tbxMessages.SelectionLength;
			// If the caret is at the bottom, it should stay there.
			bool setCaret = selectionStart >= tbxMessages.Text.Length;
			// Append the text. This resets SelectionStart and SelectionLength to 0.
			this.tbxMessages.Text += string.Format( this.formatString, System.DateTime.Now, this.indentString, category, message );
			// Maintain caret position.
			if ( setCaret )
			{
				// Put caret at the bottom.
				this.tbxMessages.SelectionStart = this.tbxMessages.Text.Length;
				this.tbxMessages.ScrollToCaret( );
			}
			else
			{
				// Restore the text selection.
				this.tbxMessages.Select( selectionStart, selectionEnd );
			}
		}

		/// <summary>
		/// Closes the form.
		/// </summary>
		/// <remarks>
		/// Called from the trace listener when the program exits.
		/// </remarks>
		public void ForceClose ( )
		{
			this.canClose = true;
			this.Close( );
		}
		
		/// <summary>
		/// Cleanup code.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TraceForm_Closing ( object sender, System.ComponentModel.CancelEventArgs e )
		{
			if ( ! this.canClose ) 
			{
				e.Cancel = true;
				this.WindowState = FormWindowState.Minimized;
			}
		}
	}
}
